package jade.imtp.leap.nio;

//#J2ME_EXCLUDE_FILE

import jade.imtp.leap.JICP.JICPPacket;
import jade.imtp.leap.JICP.JICPProtocol;
import jade.imtp.leap.http.HTTPHelper;
import jade.imtp.leap.http.HTTPRequest;
import jade.imtp.leap.http.HTTPResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Eduard Drenth: Logica, 22-sep-2009
 * 
 */
public class NIOHTTPHelper extends HTTPHelper implements BufferTransformer {

    private boolean needToRead = false;

    public static ByteBuffer readByteBufferFromHttp(InputStream is) throws IOException {
        HTTPRequest request = new HTTPRequest();
        request.readFrom(is);
        if (is.markSupported()) {
            is.mark(2);
            if (is.read() != -1) {
                is.reset();
                throw new IOException("bytes left in stream after constructing HTTPRequest");
            }
        }
        if (request.getMethod().equals("GET")) {
            String recipientID = request.getField(RECIPIENT_ID_FIELD);
            JICPPacket pkt = new JICPPacket(JICPProtocol.CONNECT_MEDIATOR_TYPE, JICPProtocol.DEFAULT_INFO, recipientID, null);
            ByteBuffer b = ByteBuffer.allocateDirect(pkt.getLength());
            MyOut out = new MyOut(b);
            pkt.writeTo(out);
            b.flip();
            return b;
        } else {
            // Read the JICPPacket from the HTTP request payload
            byte[] a = request.getPayload();
            ByteBuffer b = ByteBuffer.allocateDirect(a.length);
            MyOut out = new MyOut(b);
            out.write(a, 0, a.length);
            b.flip();
            return b;
        }
    }

    NIOJICPConnection connection;
    private static Logger log = Logger.getLogger(NIOHTTPHelper.class.getName());

    public NIOHTTPHelper(NIOJICPConnection connection) {
        this.connection = connection;
    }

    private static ByteBuffer wrapInHttpResponse(ByteBuffer pkt) throws IOException {
        byte[] b = new byte[pkt.remaining()];
        pkt.get(b, 0, pkt.remaining());
        HTTPResponse response = wrapInHttp(b);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        response.writeTo(out);
        return ByteBuffer.wrap(out.toByteArray());
    }

    private static ByteBuffer constructJICPPacket(ByteBuffer input) throws IOException {
        byte[] out = new byte[input.remaining()];
        input.get(out, 0, input.remaining());
        if (log.isLoggable(Level.FINE)) {
            log.fine("trying to read jicp packet from http: \n" + new String(out));
        }
        return readByteBufferFromHttp(new ByteArrayInputStream(out));
    }

    public ByteBuffer postprocessBufferRead(ByteBuffer data) throws IOException {
        needToRead = false;
        data.mark();
        try {
            return constructJICPPacket(data);
        } catch (EOFException ex) {
            if (log.isLoggable(Level.FINE)) {
                log.log(Level.FINE, "not enough data available, wait for more", ex);
            }
            needToRead = true;
            // incomplete, wait for more data
            data.reset();
        }
        return NIOHelper.EMPTY_BUFFER;
    }

    public ByteBuffer preprocessBufferToWrite(ByteBuffer dataToSend) throws IOException {
        return wrapInHttpResponse(dataToSend);
    }

    public boolean needSocketData() {
        return needToRead;
    }

    private static class MyOut extends ByteArrayOutputStream {

        private ByteBuffer buffer;

        public MyOut(ByteBuffer b) {
            this.buffer = b;
        }

        @Override
        public synchronized void write(int b) {
            buffer.put((byte)b);
        }

        @Override
        public synchronized void write(byte[] b, int off, int len) {
            buffer.put(b, off, len);
        }
    }
}
